package fgin

import (
	"context"
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"gitee.com/wu-jin-feng/fgin/fgin_plugs/fotel/fmeter"
	"gitee.com/wu-jin-feng/fgin/fgin_plugs/fotel/ftracer"
	"github.com/opentracing/opentracing-go"
	"github.com/prometheus/client_golang/prometheus"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/trace"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/health"
	healthpb "google.golang.org/grpc/health/grpc_health_v1"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
)

// 获取一个grpc服务
func (f *Fgin) GetGrpcServer(path string) *grpc.Server {
	// 读取配置
	readConfig(path)
	// 判断定义的协议
	if fginConfig.NetType != "grpc" {
		panic(fmt.Sprintf("您配置的net_type:%v,本方法适用的协议为:grpc", fginConfig.NetType))
	}
	// 服务名必填
	if fginConfig.ServiceName == "" {
		panic("grpc服务,service_name必填")
	}
	// tag必填
	if fginConfig.Tag == "" {
		panic("grpc服务,tag必填")
	}
	// 初始化日志
	logInit()
	// 启动通用组件
	startFginGrpcPlug()
	// 自定义组件
	PlugsGrpcStart()
	// 创建服务
	s := newGrpcServer()
	// 实现默认的健康检查
	healthServer := health.NewServer()
	healthServer.SetServingStatus(fginConfig.ServiceName, healthpb.HealthCheckResponse_SERVING)
	healthpb.RegisterHealthServer(s, healthServer)

	return s
}

func startFginGrpcPlug() {
	// opentelemetry 初始化
	initOpentelemetry()
	var wg sync.WaitGroup
	// 1. 初始化mysql gorm
	wg.Add(1)
	go func() {
		defer wg.Done()
		initGorm()
	}()
	// 2. 初始化redis
	wg.Add(1)
	go func() {
		defer wg.Done()
		initRedis()
	}()
	// 3. 初始化rabbitmq
	wg.Add(1)
	go func() {
		defer wg.Done()
		initRabbitmq()
	}()
	// 4. grpc客户端
	wg.Add(1)
	go func() {
		defer wg.Done()
		initGrpcClientV2()
	}()
	// 5. jaeger初始化
	wg.Add(1)
	go func() {
		defer wg.Done()
		initJaeger()
	}()
	// 6. 注册到consul,如果有配置
	wg.Add(1)
	go func() {
		defer wg.Done()
		initConsul()
	}()
	// 7. prometheus初始化
	wg.Add(1)
	go func() {
		defer wg.Done()
		initPrometheus()
	}()
	// 8. 神盾启动
	wg.Add(1)
	go func() {
		defer wg.Done()
		initAegis()
	}()
	// 9. 独立校验器
	wg.Add(1)
	go func() {
		defer wg.Done()
		initValidator()
	}()
	wg.Wait()
}

// 启动grpc服务
func (f *Fgin) RunGrpc(s *grpc.Server) {
	if fginConfig.Ip == "" {
		logSuger.Warn("ip没有配置,使用默认ip:127.0.0.1")
		fginConfig.Ip = "127.0.0.1"
	}
	if fginConfig.Port == 0 {
		logSuger.Warn("port没有配置,使用默认port:8081")
		fginConfig.Port = 8081
	}
	address := fmt.Sprintf("%v:%v", fginConfig.Ip, fginConfig.Port)
	listen, err := net.Listen("tcp", address)
	if err != nil {
		panic(err.Error())
	}
	defer listen.Close()
	defer rabbitmqClose()
	go func() {
		logSuger.Info(fmt.Sprintf("grpc服务%v启动成功:%v", fginConfig.ServiceName, address))
		if err := s.Serve(listen); err != nil {
			log.Fatalf("listen: %v\n", err)
		}
	}()
	// 优雅停止grpc服务
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
	<-quit
	deregisterService() // 存在consul注销服务
	logSuger.Info("Grpc Shutdown Server ...")
	s.GracefulStop()
	logSuger.Info("Grpc Server exiting")
}

// 自定义创建grpc server
func newGrpcServer() *grpc.Server {
	// 2025-04-01 将opts配置抛出，实现更多框架自由度
	// opts := []grpc.ServerOption{}
	// opts = append(opts, grpc.ConnectionTimeout(5*time.Second))
	// opts = append(opts, grpc.UnaryInterceptor(fgrpcInterceptor()))
	// 使用可注册的opts
	opts := grpcServerOptionsObj.opts
	// 默认客户端5s没有完成连接，自动断开
	if !grpcServerOptionsObj.disableDefaultTimeout {
		opts = append(opts, grpc.ConnectionTimeout(5*time.Second))
	}
	// 添加拦截器
	if !grpcServerOptionsObj.disableDefaultUnaryInterceptor {
		opts = append(opts, grpc.UnaryInterceptor(fgrpcInterceptor()))
	}
	s := grpc.NewServer(opts...)
	return s
}

// fgrpc拦截器, grpc只要只能有一个拦截器：具备日志功能
func fgrpcInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ any, err error) {
		start := time.Now()
		if info.FullMethod == "/grpc.health.v1.Health/Check" {
			// 健康检查忽略日志
			return handler(ctx, req)
		}
		var (
			// err      error
			m        any
			span     opentracing.Span
			otelSpan trace.Span
			jaegerOk = false
		)
		// otel meter 监控上报
		if fginConfig.Otel.PrometheusAddress != "" {
			code := "0"
			defer func() {
				if err != nil {
					grpcErr, ok := status.FromError(err)
					if !ok {
						// 非grpc框架产生错误, 使用400替代，表示内部逻辑异常
						code = "400"
					} else {
						code = fmt.Sprintf("%d", grpcErr.Code())
					}
				}
				meter := fmeter.NewMeter(fginConfig.ServiceName)
				meter.DefaultMetric(ctx, start, "grpc", "GRPC", info.FullMethod, code)
			}()
		}
		// otel tracer
		if fginConfig.Otel.TracerHttp != "" {
			tracer := ftracer.NewTracer(trace.SpanKindServer, info.FullMethod)
			ctx, otelSpan = tracer.Start(ctx, info.FullMethod)
			otelSpan.SetAttributes(
				attribute.String("method", "GRPC"),
			)
			defer func() { tracer.End(otelSpan, err) }()
		}
		// 处理超时
		if fginConfig.Timeout > 0 {
			t, ok := ctx.Deadline()
			var (
				cancel context.CancelFunc
			)
			remainMs := fginConfig.Timeout
			if ok && !fginConfig.DisableRequestTimeout {
				// 继承上游服务的耗时，和配置中的对比，优先小值
				remainMs = min(fginConfig.Timeout, int(time.Until(t).Milliseconds()))
			}
			ctx, cancel = context.WithTimeout(ctx, time.Duration(remainMs)*time.Millisecond)
			defer cancel()
		}
		// 抓异常
		defer func() {
			if errRec := recover(); errRec != nil {
				if (fginConfig.Log == Log{} || logSuger == nil) {
					fmt.Println(errRec)
				} else {
					logSuger.Error(errRec, ctx)
				}
				// panic 500异常
				var panicCode codes.Code = 500
				err = status.Error(panicCode, "grpc service error")
			}
		}()
		// 限流中间件
		if err = fgrpcServerRateLimiterMiddlware(ctx, info.FullMethod); err != nil {
			return nil, err
		}
		// 熔断中间件
		if err = fgrpcCircuitBreakerMiddlware(ctx, info.FullMethod); err != nil {
			return nil, err
		}
		// 实现jaeger上报
		if (fginConfig.Jaeger != Jaeger{}) {
			span, ctx, jaegerOk = jaegerInitGrpc(ctx, info.FullMethod)
			if jaegerOk {
				span.SetTag("method", "GRPC")
				defer span.Finish()
			}
		}
		// prometheus监控
		if (fginConfig.Prometheus != Prometheus{}) {
			code := "0"
			defer func() {
				if err != nil {
					grpcErr, ok := status.FromError(err)
					if !ok {
						// 非grpc框架产生错误, 使用400替代，表示内部逻辑异常
						code = "400"
					} else {
						code = fmt.Sprintf("%d", grpcErr.Code())
					}
				}
				// 计数
				requestCount.WithLabelValues("grpc", info.FullMethod, code).Inc()
				// 耗时
				obs := requestHistogram.WithLabelValues("grpc", info.FullMethod, code)
				timer := prometheus.NewTimer(obs)
				timer.ObserveDuration()
			}()
		}
		// grpc自定义拦截器
		ctx, err = PlugsGrpcMiddStart(ctx, req, info)
		if err != nil {
			return nil, err
		}
		// 运行grpc方法
		m, err = handler(ctx, req)
		// grpc 自定义拦截器 执行后
		if err == nil {
			err = PlugsGrpcMiddAfterStart(ctx, req, info, err)
		}
		// 耗时
		consTime := time.Since(start)
		if err != nil {
			if (fginConfig.Log != Log{}) {
				logInfo := fmt.Sprintf("RPC:%s, cons:%s, req:%v, err:%v", info.FullMethod, consTime, req, err)
				logSuger.Error(logInfo, ctx)
			}
			if jaegerOk {
				// 使用jaeger，报错需要上报
				span.SetTag("error", true)
				span.LogKV("error_message", err.Error())
			}
			// 熔断器标记失败
			circuitBreaker.MarkFailed()
		} else {
			if (fginConfig.Log != Log{}) {
				logInfo := fmt.Sprintf("RPC:%s, cons:%s, req:%v, err:%v", info.FullMethod, consTime, req, err)
				logSuger.Info(logInfo, ctx)
			}
			// 熔断器标记成功
			circuitBreaker.MarkSuccess()
		}
		return m, err
	}
}

func jaegerInitGrpc(ctx context.Context, service string) (opentracing.Span, context.Context, bool) {
	var tracer opentracing.Tracer
	if opentracing.IsGlobalTracerRegistered() {
		tracer = opentracing.GlobalTracer()
	} else {
		logSuger.Error("http jaeger GlobalTracer is undefine", ctx)
		return nil, ctx, false
	}
	md, _ := metadata.FromIncomingContext(ctx)
	spanCtx, err := opentracing.GlobalTracer().Extract(
		opentracing.HTTPHeaders,
		opentracing.HTTPHeadersCarrier(md),
	)
	var span opentracing.Span
	if err == nil {
		span = opentracing.StartSpan(service, opentracing.ChildOf(spanCtx))
	} else {
		span = tracer.StartSpan(service)
	}
	newCtx := opentracing.ContextWithSpan(ctx, span)
	return span, newCtx, true
}

// Deprecated: 放入到默认的一元中间件实现
// 处理panic异常
func recoverInit(ctx context.Context) error {
	if err := recover(); err != nil {
		if (fginConfig.Log == Log{} || logSuger == nil) {
			fmt.Println(err)
		} else {
			logSuger.Error(err, ctx)
		}
		return fmt.Errorf("grpc service error")
	}
	return nil
}

// 限流器
func fgrpcServerRateLimiterMiddlware(ctx context.Context, method string) error {
	if err := rateLimiter.Allow(); err != nil {
		logSuger.Info(fginConfig.ServiceName+":"+method+rateErrMsg, ctx)
		return status.Error(rateCode, fginConfig.ServiceName+":"+method+rateErrMsg)
	}
	return nil
}

// 熔断器
func fgrpcCircuitBreakerMiddlware(ctx context.Context, method string) error {
	if err := circuitBreaker.Allow(); err != nil {
		logSuger.Info(fginConfig.ServiceName+":"+method+circuitErrMsg, ctx)
		return status.Error(circuitCode, fginConfig.ServiceName+":"+method+circuitErrMsg)
	}
	return nil
}
